using MongoDB.Driver;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.CompanyReportSyncStep.CompanyReportOnline;
using Sustainalytics.CompanyReportSyncStep.Exceptions;
using Sustainalytics.DataLayer.EsgRatings;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESG.Entities;
using Sustainalytics.ESGRatings.BusinessLogic;
using Sustainalytics.ESGRatings.BusinessLogic.ESGClient;
using Sustainalytics.ESGRatings.BusinessLogic.ESGProfile;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sustainalytics.ReportGenerator.BL
{
    public class ReportDataPreparationSyncStep : ISyncStep
    {
        private int _maxDegreeOfParallelism;

        public ReportDataPreparationSyncStep()
        {
            _bulkSize = 50;
        }

        public ReportDataPreparationSyncStep(int bulkSize)
        {
            _bulkSize = bulkSize;
        }

        public SyncStepOrder ExecutionOrder => SyncStepOrder.ReportDataPreparation;

        public SyncContext Start(SyncContext syncContext)
        {
            syncContext.Log("PdfReportGeneration.Start  _bulkSize = " + _bulkSize);
            _maxDegreeOfParallelism = ParallelHelper.GetMaxDegreeOfParallelism();

            // load connection string from context
            string esgComputeAddressString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ComputationServiceAddress", out esgComputeAddressString))
                throw new ArgumentException("ComputationServiceAddress key is missing!");

            string clientDWConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientDW", out clientDWConnectionString))
                throw new ArgumentException("ClientDW key is missing!");

            string clientUniversesConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientUniverses", out clientUniversesConnectionString))
                throw new ArgumentException("ClientUniverses key is missing!");

            string clientPlatformConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("ClientPlatform", out clientPlatformConnectionString))
                throw new ArgumentException("ClientPlatform key is missing!");

            string dwArchiveConnectionString;
            if (!syncContext.GAConnectionStrings.TryGetValue("DWArchive", out dwArchiveConnectionString))
                throw new ArgumentException("DWArchive key is missing!");

            // start
            var sw = Stopwatch.StartNew();

            // allocate repositories
            var keyRepositories = KeyEsgIssuesRepository.GetInstance(clientDWConnectionString);
            var keiPersistance = new KEIPersistance(keyRepositories);

            syncContext.Log("Acquiring default profile");
            var esgDefaultProfileManagement =
                  new ESGProfileManagement(
                      new RepositoryCollection<ESGProfile>(clientPlatformConnectionString),
                      new RepositoryCollection<ReferenceUniverse>(clientDWConnectionString),
                      new RepositoryCollection<WeightMatrix>(clientDWConnectionString));

            var defaultEsgProfile = esgDefaultProfileManagement.LoadDefaultEsgProfile();

            if (defaultEsgProfile == null)
            {
                syncContext.Log("Default profile not found");
                throw new DefaultProfileNotFoundException("default profile not found");
            }
            syncContext.Log("Default ESG Profile:  " + defaultEsgProfile);

            syncContext.Token.ThrowIfCancellationRequested();

            //Read necessary aditional data

            var corporateDataManagement = new CorporateDataManagement(
                MongoFactory.GetCollectionFromDatabase<CorporateData>(clientDWConnectionString),
                MongoFactory.GetCollectionFromDatabase<RPESGResearchState>(clientDWConnectionString));

            var allCompaniesWithEsgResearch = corporateDataManagement.GetCorporateDataIdsWithEsgResearch().Result;

            var keyEsgIssuesDictionary = GetCompanyKeyIsssues(
                MongoFactory.GetCollectionFromDatabase<CompanyKeyESGIssue>(clientDWConnectionString),
                allCompaniesWithEsgResearch).Result;

            var companiesWithEsgResearchForProfile = corporateDataManagement.GetFilteredCorporateDataIdsWithEsgResearch(
                null,
                null,
                defaultEsgProfile.AccountId,
                defaultEsgProfile.Id,
                defaultEsgProfile.MatrixId).Result;

            var financialDataForProfile = GetCompanyFinancialData(MongoFactory.GetCollectionFromDatabase<FinancialData>(clientDWConnectionString), companiesWithEsgResearchForProfile).Result;

            #region esgComputation and population

            var setId = GetSyncSetId(syncContext);

            Guid loadedSetId;

            using (var esgClient = new ESGClient(esgComputeAddressString))
            {
                loadedSetId = esgClient.LoadDataAsync(setId).Result;
            }

            syncContext.Log($"Using for computing set with id {loadedSetId}");

            var esgRatingsCache = new ESGRatingsComputationsClient(esgComputeAddressString, defaultEsgProfile, loadedSetId);

            var esgRatingsTask = esgRatingsCache.GetRatingsAsync();

            UpdateFinancialData(syncContext, esgRatingsTask, financialDataForProfile).Wait();

            var exceptions = new List<Exception>();
            var KEICount = 0;

            var computingDataPersistance = EsgComputationPersistance(MongoFactory.GetCollectionFromDatabase<RPCompanyESGComputationResult>(clientDWConnectionString));

            var historicalDataLoader = new HistoricalDataLoader(MongoFactory.GetCollectionFromDatabase<HistoricalData>(dwArchiveConnectionString));

            var companiesCount = 0;

            foreach (var companiesIds in companiesWithEsgResearchForProfile.Batch(_bulkSize))
            {
                var companiesBatch = companiesIds as Guid[] ?? companiesIds.ToArray();
                companiesCount += companiesBatch.Count();

                foreach (var companyId in companiesBatch)

                // Parallel.ForEach(companiesBatch, new ParallelOptions { CancellationToken = syncContext.Token, MaxDegreeOfParallelism = _maxDegreeOfParallelism }, (companyId) =>
                {
                    GetEsgData(syncContext, esgRatingsTask, computingDataPersistance, exceptions, companyId, historicalDataLoader).Wait();

                    KEICount = GetKeyIssues(syncContext, esgRatingsTask, keyEsgIssuesDictionary, keiPersistance, exceptions, KEICount, companyId).Result;
                }
                //);
            }

            ClearComputingCollection(MongoFactory.GetCollectionFromDatabase<RPCompanyESGComputationResult>(clientDWConnectionString), loadedSetId).Wait();

            if (exceptions.Count > 0)
            {
                exceptions.ForEach(ex => syncContext.Log("ReportDataPreparationSyncStep.Start.action: " + ex.ToString()));
                if (syncContext.ErrorPolicy == ErrorPolicy.BreakRightAway)
                {
                    var ae = new AggregateException(exceptions);
                    throw ae.Flatten();
                }
            }

            syncContext.Log("Updating KEY ESG Issues, documents count: " + KEICount);

            keiPersistance.Complete();
            try
            {
                keiPersistance.Completion.Wait();
            }
            catch (AggregateException ae)
            {
                syncContext.Log(ae.Flatten().ToString());
                if (syncContext.ErrorPolicy == ErrorPolicy.BreakRightAway)
                    throw ae.Flatten();
            }
            sw.Stop();

            #endregion

            syncContext.Log($"Report data preparation completed. Elapsed time: {sw.Elapsed}, Companies: {companiesCount}");

            return syncContext;
        }

        private static Task ClearComputingCollection(IMongoCollection<RPCompanyESGComputationResult> mongoCollection, Guid setId)
        {
            return mongoCollection.DeleteManyAsync(x => x.SetId != setId && x.SetId != Guid.Empty);
        }

        private static async Task<Dictionary<Guid, List<KeyEsgIssue>>> GetCompanyKeyIsssues(IMongoCollection<CompanyKeyESGIssue> keiRepositoryCollection, List<Guid> allCompaniesWithEsgResearch)
        {
            var keyEsgIssuesDictionary = new Dictionary<Guid, List<KeyEsgIssue>>();

            var companyKeyEsgIssues = await keiRepositoryCollection.Find(x => allCompaniesWithEsgResearch.Contains(x.Id)).ToListAsync();
            if (!companyKeyEsgIssues.IsNullOrEmpty())
            {
                keyEsgIssuesDictionary = companyKeyEsgIssues.ToDictionary(kei => kei.Id, kei => kei.KeyESGIssues);
            }

            return keyEsgIssuesDictionary;
        }

        private static async Task<Dictionary<Guid, FinancialData>> GetCompanyFinancialData(IMongoCollection<FinancialData> financialDataCollection, List<Guid> companiesWithEsgResearch)
        {
            var financialData = await financialDataCollection.Find(x => companiesWithEsgResearch.Contains(x.Id)).ToListAsync();
            return financialData.ToDictionary(x => x.Id);
        }

        private static Guid GetSyncSetId(SyncContext syncContext)
        {
            var syncContextInfo = syncContext.PreviousChanges.FirstOrDefault(i => i.AffectedObjectName == ChangedElement.DATA_SET_DELETE_SYNC_FAIL);
            var currentChangesInfo = syncContext.CurrentChanges.FirstOrDefault(i => i.AffectedObjectName == ChangedElement.DATA_SET_DELETE_SYNC_FAIL);
            return syncContextInfo?.AffectedDocumentId ?? (currentChangesInfo?.AffectedDocumentId ?? Guid.Empty);
        }

        private static async Task<int> GetKeyIssues(
            SyncContext syncContext,
            Task<ESGRatingsAdapter> esgRatingsTask,
            Dictionary<Guid, List<KeyEsgIssue>> keyEsgIssuesDictionary,
            KEIPersistance keiPersistance,
            List<Exception> exceptions,
            int keiCount,
            Guid companyId)
        {
            try
            {
                var esgData = await esgRatingsTask;
                var k = 0;

                //TODO: remove KEIESGISSUES this should be removed

                if (keyEsgIssuesDictionary != null)
                {
                    var list = new List<KeyIndicatorData>();
                    List<KeyEsgIssue> keyEsgIssues;
                    if (keyEsgIssuesDictionary.TryGetValue(companyId, out keyEsgIssues))
                    {
                        foreach (var item in keyEsgIssues)
                        {
                            if (item.KeyEsgIssueIndicators == null)
                            {
                                continue;
                            }

                            foreach (var keiIndicator in item.KeyEsgIssueIndicators)
                            {
                                if (esgData.FillIndicatorsAggregates(companyId, keiIndicator))
                                {
                                    if (list.Any(a => a.IndicatorNumber == keiIndicator.Number))
                                    {
                                        var values = list.Where(a => a.IndicatorNumber == keiIndicator.Number).Select(a => a.ToString()).Aggregate((a, b) => $"{a};{b}");
                                        var strCurrent = $"IndicatorNumber: {keiIndicator.Number}, CompanyScore: {keiIndicator.Score}, Average: {keiIndicator.Average}, BestPractice: {keiIndicator.BestPractice}, MaterialBestPractice: {keiIndicator.MaterialBestPracticeGap}, AnswerCategory: {string.Empty}";
                                        syncContext.Log($"Duplicate key esg sent:{strCurrent}. There are already esg added:{values}");
                                        continue;
                                    }
                                    list.Add(new KeyIndicatorData
                                    {
                                        IndicatorNumber = keiIndicator.Number,
                                        CompanyScore = keiIndicator.Score,
                                        Average = keiIndicator.Average,
                                        BestPractice = keiIndicator.BestPractice,
                                        MaterialBestPractice = keiIndicator.MaterialBestPracticeGap
                                    });
                                    k++;
                                }
                            }
                        }

                        keiPersistance.Push(companyId, list);
                    }
                }

                Interlocked.Add(ref keiCount, k);
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                exceptions.Add(ex);
            }
            catch (Exception ex)
            {
                syncContext.Log($"ReportDataPreparationSyncStep.Start: for {companyId} we got: {ex} ");
                exceptions.Add(ex);
            }

            return keiCount;
        }

        private static async Task GetEsgData(
            SyncContext syncContext,
            Task<ESGRatingsAdapter> esgRatingsTask,
            Func<RPCompanyESGComputationResult, Task> persistanceAction,
            List<Exception> exceptions,
            Guid companyId,
            HistoricalDataLoader historicalDataLoader)
        {
            try
            {
                var esgRatingsData = await esgRatingsTask;
                var computationResult = new RPCompanyESGComputationResult { Id = companyId, SetId = esgRatingsData.SetId };

                var esgData = esgRatingsData.GetEsgData(companyId);

                if (esgData == null)
                {
                    syncContext.Log($"Computation data not found for company {companyId}");
                }
                else
                {
                    computationResult.IsInReferenceUniverse = esgData.IsInReferenceUniverse;
                    computationResult.OverallPerformance = esgRatingsData.GetOverallPerformance(companyId);
                    computationResult.EnvironmentPerformance = esgRatingsData.GetEnvironmentPerformance(companyId);
                    computationResult.SocialPerformance = esgRatingsData.GetSocialPerformance(companyId);
                    computationResult.GovernancePerformance = esgRatingsData.GetGovernancePerformance(companyId);
                    computationResult.DisclosureScore = esgRatingsData.GetDisclosurePerformance(companyId);
                    computationResult.PreparednessScore = esgRatingsData.GetPreparednessPerformance(companyId);
                    computationResult.QualitativePerformanceScore = esgRatingsData.GetQualitativePerformance(companyId);
                    computationResult.QuantitativePerformanceScore = esgRatingsData.GetQuantitativePerformance(companyId);
                    var industryTop5 = esgRatingsData.ComparativeGroupTop5ByCompanyId(companyId).Select(x => new RPCorporateScore
                    {
                        CompanyId = x.CompanyId,
                        Score = x.Score,
                        CompanyName = x.CompanyName,
                        Rank = x.Rank,
                    });
                    var companyPeers = esgRatingsData.GetIndustryPeers(companyId);

                    var peerList = companyPeers.Select(x => new RPCorporateScore
                    {
                        CompanyId = x.CompanyId,
                        Score = x.Score,
                        CompanyName = x.CompanyName,
                        Rank = x.Rank,
                    }).ToList();

                    var financialInfo = esgRatingsData.ComputeMarketCapitalization(companyPeers);

                    computationResult.RelativePosition = new RPRelativePosition
                    {
                        TopList = industryTop5.ToArray(),
                        PeersList = peerList.ToArray(),
                        MarketCapRangeFrom = financialInfo.Item1,
                        MarketCapRangeTo = financialInfo.Item2
                    };

                    var lastMonthData = await historicalDataLoader.GetLastMonthHistoricalData(companyId).ConfigureAwait(false);
                    var lastYearData = await historicalDataLoader.GetLastYearHistoricalData(companyId).ConfigureAwait(false);
                    computationResult.OverallPerformance.SetMoMMomentumPercentile(lastMonthData.OverallPercentile, lastMonthData.Date);
                    computationResult.EnvironmentPerformance.SetMoMMomentumPercentile(lastMonthData.EPercentile, lastMonthData.Date);
                    computationResult.SocialPerformance.SetMoMMomentumPercentile(lastMonthData.SPercentile, lastMonthData.Date);
                    computationResult.GovernancePerformance.SetMoMMomentumPercentile(lastMonthData.GPercentile, lastMonthData.Date);

                    computationResult.OverallPerformance.SetYoYMomentumPercentile(lastYearData.OverallPercentile, lastYearData.Date);
                    computationResult.EnvironmentPerformance.SetYoYMomentumPercentile(lastYearData.EPercentile, lastYearData.Date);
                    computationResult.SocialPerformance.SetYoYMomentumPercentile(lastYearData.SPercentile, lastYearData.Date);
                    computationResult.GovernancePerformance.SetYoYMomentumPercentile(lastYearData.GPercentile, lastYearData.Date);

                    computationResult.OverallPerformance.SetMoMMomentumScore(lastMonthData.OverallScore, lastMonthData.Date);
                    computationResult.EnvironmentPerformance.SetMoMMomentumScore(lastMonthData.EScore, lastMonthData.Date);
                    computationResult.SocialPerformance.SetMoMMomentumScore(lastMonthData.SScore, lastMonthData.Date);
                    computationResult.GovernancePerformance.SetMoMMomentumScore(lastMonthData.GScore, lastMonthData.Date);

                    computationResult.OverallPerformance.SetYoYMomentumScore(lastYearData.OverallScore, lastYearData.Date);
                    computationResult.EnvironmentPerformance.SetYoYMomentumScore(lastYearData.EScore, lastYearData.Date);
                    computationResult.SocialPerformance.SetYoYMomentumScore(lastYearData.SScore, lastYearData.Date);
                    computationResult.GovernancePerformance.SetYoYMomentumScore(lastYearData.GScore, lastYearData.Date);

                    await persistanceAction(computationResult);
                }
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                exceptions.Add(ex);
            }
            catch (Exception ex)
            {
                syncContext.Log($"ReportDataPreparationSyncStep.Start: for {companyId} we got: {ex} ");
                exceptions.Add(ex);
            }
        }

        private static async Task UpdateFinancialData(SyncContext syncContext, Task<ESGRatingsAdapter> esgRatingsTask, Dictionary<Guid, FinancialData> financialData)
        {
            try
            {
                var clientDWConnectionString = syncContext.GAConnectionStrings["ClientDW"];
                var thresholdsmanager = new EsgThresholdManager<EsgFocusThresholds, string>(clientDWConnectionString);
                var taskResult = await esgRatingsTask.ConfigureAwait(false);
                var treshholds = thresholdsmanager.GetCompanyEsgThresholds();
                var count = taskResult.LoadFinancialData(financialData);
                taskResult.LoadEsgFocus(treshholds);
                syncContext.Log(string.Format("Updated AllFinancialData for {0} records", count));
            }
            catch (AggregateException ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                throw;
            }
            catch (Exception ex)
            {
                syncContext.Log(ExceptionUtility.GetInfo(ex, true));
                throw;
            }
        }

        private Func<RPCompanyESGComputationResult, Task> EsgComputationPersistance(IMongoCollection<RPCompanyESGComputationResult> resultRepositoryCollection)
        {
            return resultBatch =>
            {
                var builder = Builders<RPCompanyESGComputationResult>.Update.Set(x => x.IsInReferenceUniverse, resultBatch.IsInReferenceUniverse)
                .Set(x => x.OverallPerformance.Avg, resultBatch.OverallPerformance.Avg)
                .Set(x => x.OverallPerformance.Min, resultBatch.OverallPerformance.Min)
                .Set(x => x.OverallPerformance.Max, resultBatch.OverallPerformance.Max)
                .Set(x => x.OverallPerformance.GroupCount, resultBatch.OverallPerformance.GroupCount)
                .Set(x => x.OverallPerformance.Rank, resultBatch.OverallPerformance.Rank)
                .Set(x => x.OverallPerformance.ScoreMomentumYoY, resultBatch.OverallPerformance.ScoreMomentumYoY)
                .Set(x => x.OverallPerformance.ScoreMomentumMoM, resultBatch.OverallPerformance.ScoreMomentumMoM)
                .Set(x => x.OverallPerformance.Percentile, resultBatch.OverallPerformance.Percentile)
                .Set(x => x.OverallPerformance.PercentileMomentumMoM, resultBatch.OverallPerformance.PercentileMomentumMoM)
                .Set(x => x.OverallPerformance.PercentileMomentumYoY, resultBatch.OverallPerformance.PercentileMomentumYoY)

                .Set(x => x.SocialPerformance.Avg, resultBatch.SocialPerformance.Avg)
                .Set(x => x.SocialPerformance.Min, resultBatch.SocialPerformance.Min)
                .Set(x => x.SocialPerformance.Max, resultBatch.SocialPerformance.Max)
                .Set(x => x.SocialPerformance.GroupCount, resultBatch.SocialPerformance.GroupCount)
                .Set(x => x.SocialPerformance.Rank, resultBatch.SocialPerformance.Rank)
                .Set(x => x.SocialPerformance.ScoreMomentumYoY, resultBatch.SocialPerformance.ScoreMomentumYoY)
                .Set(x => x.SocialPerformance.ScoreMomentumMoM, resultBatch.SocialPerformance.ScoreMomentumMoM)
                .Set(x => x.SocialPerformance.Percentile, resultBatch.SocialPerformance.Percentile)
                .Set(x => x.SocialPerformance.PercentileMomentumMoM, resultBatch.SocialPerformance.PercentileMomentumMoM)
                .Set(x => x.SocialPerformance.PercentileMomentumYoY, resultBatch.SocialPerformance.PercentileMomentumYoY)

                .Set(x => x.EnvironmentPerformance.Avg, resultBatch.EnvironmentPerformance.Avg)
                .Set(x => x.EnvironmentPerformance.Min, resultBatch.EnvironmentPerformance.Min)
                .Set(x => x.EnvironmentPerformance.Max, resultBatch.EnvironmentPerformance.Max)
                .Set(x => x.EnvironmentPerformance.GroupCount, resultBatch.EnvironmentPerformance.GroupCount)
                .Set(x => x.EnvironmentPerformance.Rank, resultBatch.EnvironmentPerformance.Rank)
                .Set(x => x.EnvironmentPerformance.ScoreMomentumYoY, resultBatch.EnvironmentPerformance.ScoreMomentumYoY)
                .Set(x => x.EnvironmentPerformance.ScoreMomentumMoM, resultBatch.EnvironmentPerformance.ScoreMomentumMoM)
                .Set(x => x.EnvironmentPerformance.Percentile, resultBatch.EnvironmentPerformance.Percentile)
                .Set(x => x.EnvironmentPerformance.PercentileMomentumMoM, resultBatch.EnvironmentPerformance.PercentileMomentumMoM)
                .Set(x => x.EnvironmentPerformance.PercentileMomentumYoY, resultBatch.EnvironmentPerformance.PercentileMomentumYoY)

                .Set(x => x.GovernancePerformance.Avg, resultBatch.GovernancePerformance.Avg)
                .Set(x => x.GovernancePerformance.Min, resultBatch.GovernancePerformance.Min)
                .Set(x => x.GovernancePerformance.Max, resultBatch.GovernancePerformance.Max)
                .Set(x => x.GovernancePerformance.GroupCount, resultBatch.GovernancePerformance.GroupCount)
                .Set(x => x.GovernancePerformance.Rank, resultBatch.GovernancePerformance.Rank)
                .Set(x => x.GovernancePerformance.ScoreMomentumYoY, resultBatch.GovernancePerformance.ScoreMomentumYoY)
                .Set(x => x.GovernancePerformance.ScoreMomentumMoM, resultBatch.GovernancePerformance.ScoreMomentumMoM)
                .Set(x => x.GovernancePerformance.Percentile, resultBatch.GovernancePerformance.Percentile)
                .Set(x => x.GovernancePerformance.PercentileMomentumMoM, resultBatch.GovernancePerformance.PercentileMomentumMoM)
                .Set(x => x.GovernancePerformance.PercentileMomentumYoY, resultBatch.GovernancePerformance.PercentileMomentumYoY)

                .Set(x => x.PreparednessScore.Avg, resultBatch.PreparednessScore.Avg)
                .Set(x => x.PreparednessScore.Min, resultBatch.PreparednessScore.Min)
                .Set(x => x.PreparednessScore.Max, resultBatch.PreparednessScore.Max)
                .Set(x => x.PreparednessScore.GroupCount, resultBatch.PreparednessScore.GroupCount)
                .Set(x => x.PreparednessScore.Rank, resultBatch.PreparednessScore.Rank)
                .Set(x => x.PreparednessScore.ScoreMomentumYoY, resultBatch.PreparednessScore.ScoreMomentumYoY)
                .Set(x => x.PreparednessScore.ScoreMomentumMoM, resultBatch.PreparednessScore.ScoreMomentumMoM)
                .Set(x => x.PreparednessScore.Percentile, resultBatch.PreparednessScore.Percentile)
                .Set(x => x.PreparednessScore.PercentileMomentumMoM, resultBatch.PreparednessScore.PercentileMomentumMoM)
                .Set(x => x.PreparednessScore.PercentileMomentumYoY, resultBatch.PreparednessScore.PercentileMomentumYoY)

                .Set(x => x.QualitativePerformanceScore.Avg, resultBatch.QualitativePerformanceScore.Avg)
                .Set(x => x.QualitativePerformanceScore.Min, resultBatch.QualitativePerformanceScore.Min)
                .Set(x => x.QualitativePerformanceScore.Max, resultBatch.QualitativePerformanceScore.Max)
                .Set(x => x.QualitativePerformanceScore.GroupCount, resultBatch.QualitativePerformanceScore.GroupCount)
                .Set(x => x.QualitativePerformanceScore.Rank, resultBatch.QualitativePerformanceScore.Rank)
                .Set(x => x.QualitativePerformanceScore.ScoreMomentumYoY, resultBatch.QualitativePerformanceScore.ScoreMomentumYoY)
                .Set(x => x.QualitativePerformanceScore.ScoreMomentumMoM, resultBatch.QualitativePerformanceScore.ScoreMomentumMoM)
                .Set(x => x.QualitativePerformanceScore.Percentile, resultBatch.QualitativePerformanceScore.Percentile)
                .Set(x => x.QualitativePerformanceScore.PercentileMomentumMoM, resultBatch.QualitativePerformanceScore.PercentileMomentumMoM)
                .Set(x => x.QualitativePerformanceScore.PercentileMomentumYoY, resultBatch.QualitativePerformanceScore.PercentileMomentumYoY)

                .Set(x => x.QuantitativePerformanceScore.Avg, resultBatch.QuantitativePerformanceScore.Avg)
                .Set(x => x.QuantitativePerformanceScore.Min, resultBatch.QuantitativePerformanceScore.Min)
                .Set(x => x.QuantitativePerformanceScore.Max, resultBatch.QuantitativePerformanceScore.Max)
                .Set(x => x.QuantitativePerformanceScore.GroupCount, resultBatch.QuantitativePerformanceScore.GroupCount)
                .Set(x => x.QuantitativePerformanceScore.Rank, resultBatch.QuantitativePerformanceScore.Rank)
                .Set(x => x.QuantitativePerformanceScore.ScoreMomentumYoY, resultBatch.QuantitativePerformanceScore.ScoreMomentumYoY)
                .Set(x => x.QuantitativePerformanceScore.ScoreMomentumMoM, resultBatch.QuantitativePerformanceScore.ScoreMomentumMoM)
                .Set(x => x.QuantitativePerformanceScore.Percentile, resultBatch.QuantitativePerformanceScore.Percentile)
                .Set(x => x.QuantitativePerformanceScore.PercentileMomentumMoM, resultBatch.QuantitativePerformanceScore.PercentileMomentumMoM)
                .Set(x => x.QuantitativePerformanceScore.PercentileMomentumYoY, resultBatch.QuantitativePerformanceScore.PercentileMomentumYoY)

                .Set(x => x.DisclosureScore.Avg, resultBatch.DisclosureScore.Avg)
                .Set(x => x.DisclosureScore.Min, resultBatch.DisclosureScore.Min)
                .Set(x => x.DisclosureScore.Max, resultBatch.DisclosureScore.Max)
                .Set(x => x.DisclosureScore.GroupCount, resultBatch.DisclosureScore.GroupCount)
                .Set(x => x.DisclosureScore.Rank, resultBatch.DisclosureScore.Rank)
                .Set(x => x.DisclosureScore.ScoreMomentumYoY, resultBatch.DisclosureScore.ScoreMomentumYoY)
                .Set(x => x.DisclosureScore.ScoreMomentumMoM, resultBatch.DisclosureScore.ScoreMomentumMoM)
                .Set(x => x.DisclosureScore.Percentile, resultBatch.DisclosureScore.Percentile)
                .Set(x => x.DisclosureScore.PercentileMomentumMoM, resultBatch.DisclosureScore.PercentileMomentumMoM)
                .Set(x => x.DisclosureScore.PercentileMomentumYoY, resultBatch.DisclosureScore.PercentileMomentumYoY)

                .Set(x => x.RelativePosition, resultBatch.RelativePosition)
                .Set(x => x.SetId, resultBatch.SetId);
                return resultRepositoryCollection.UpdateOneAsync(x => x.Id == resultBatch.Id, builder);
            };
        }

        private readonly int _bulkSize;
    }

    public class HistoricalDataLoader
    {
        public HistoricalDataLoader(IMongoCollection<HistoricalData> mongoCollection)
        {
            this.mongoCollection = mongoCollection;
        }

        private readonly IMongoCollection<HistoricalData> mongoCollection;

        private DateTime LastYearDate => new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMonths(-11);

        private DateTime LastMonthDate => new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 0, 0, 0, 0, DateTimeKind.Utc);

        public async Task<HistoricalData> GetLastYearHistoricalData(Guid companyId)
        {
            var result = await mongoCollection.Find(x => x.CompanyId == companyId && x.Date <= LastYearDate).SortByDescending(x => x.Date).FirstOrDefaultAsync().ConfigureAwait(false);
            if (result == null)
            {
                return new HistoricalData(LastYearDate, companyId);
            }
            return result;
        }

        public async Task<HistoricalData> GetLastMonthHistoricalData(Guid companyId)
        {
            var orderedFindFluent = mongoCollection.Find(x => x.CompanyId == companyId && x.Date <= LastMonthDate).SortByDescending(x => x.Date);
            var result = await orderedFindFluent.FirstOrDefaultAsync().ConfigureAwait(false);
            if (result == null)
            {
                return new HistoricalData(LastMonthDate, companyId);
            }
            return result;
        }
    }
}
